#==================================================
#	Setget
#==================================================
# @path: res://addons/code_editor_menu_button/plugin/setget/Setget.gd
# @datetime: 2021-7-30 18:31:52
#==================================================
tool
extends WindowDialog


const SetgetCheckBox = preload("res://addons/code_editor_menu_button/plugin/setget/SetgetCheckBox.tscn")

const SetGetPattern = "setget\\s+(?<set>\\w+)?\\s*(,\\s*(?<get>\\w+))?"


onready var node_checkboxlist = $Margin/VBox/HBox/Panel/Scroll/CheckBoxList
onready var append = $Margin/VBox/VBox/Append
onready var setter = $Margin/VBox/HBoxSelectCheckBox/Setter
onready var getter = $Margin/VBox/HBoxSelectCheckBox/Getter
onready var trim_both_sides_underline = $Margin/VBox/HBoxSelectCheckBox/TrimBothSidesUnderline


var edit_util := preload("res://addons/code_editor_menu_button/util/script_editor_util.gd").new()
var script_util := preload("res://addons/code_editor_menu_button/util/script_util.gd")

var property_list_data := []
var textedit : TextEdit



#==================================================
#   Set/Get 方法
#==================================================
func set_interface(interface: EditorInterface):
	edit_util.set_interface(interface)


##  获取属性数据列表
## @return 返回代码解析后的数据
func get_editor_property_data_list() -> Array:
	var regex = RegEx.new()
	var pattern = (
		"^(?<code>"
		
		# 判断开头
		+ "("
			# export 开头
			+ "export\\s*"
			+ "(?<hint>\\([^\\)]+\\)\\s*)?" # hint 导出标识
			+ "var\\s+"		# var
			
			#onready 开头
			+ "|"
			+ "onready\\s+"
			+ "var\\s+?"		# var
			
			# var 开头
			+ "|"
			+ "var\\s+"
		+ ")"
		
		+ "(?<name>[^\\s:]+)\\s*?"	# name 方法名
		+ "(\\s*:\\s*)?"		# : 冒号
		+ "(?<type>\\w+)?"		# type 类型
		
		# 变量值
		+ "(\\s*=\\s*"	# 等号
			+ "(?<value>[^#\n\\s]+)?"	# 直到不是 # 或者回车的位置
			+ ")?"
		
		# setget
		+ "(?<setget>\\s*setget\\s+(\\w+)?(\\s*,\\s*\\w+)?)?"
		
		+ ")"	# 代码
		
		
		+ "(?<comments>\\s*#[^\n]*)?"	# 注释
	)
	regex.compile(pattern)
	
	var text = ""
	var data_list = []
	for line in textedit.get_line_count():
		text = textedit.get_line(line)
		var r_match : RegExMatch = regex.search(text)
		if r_match:
			var data = {
				code = r_match.get_string("code"),
				name = r_match.get_string("name"),
				value = r_match.get_string("value"),
				type = r_match.get_string("type"),
				setget = r_match.get_string("setget"),
				hint = r_match.get_string("hint"),
				comments = r_match.get_string("comments"),
				line = line,
			}
			data_list.push_back(data)
	
	return data_list


##  返回 Set 方法
## @property  属性名
## @type  类型
## @return 返回生成的 Set 方法
func generate_set_method(
	property: String, 
	type: String =""
) -> Dictionary:
	# 清除两边其他字符
	var method_property = ""
	if trim_both_sides_underline.pressed:
		method_property = property.strip_edges().trim_prefix("_").trim_suffix("_")
	else:
		method_property = property.strip_edges()
	
	# 生成方法与类型数据
	var method = "set_" + method_property
	type = type.strip_edges()
	if type != "":
		type = ": " + type
	
	var data = {
		property = property,
		method = method,
		type = type,
	}
	var code = """func {method}(value{type}) -> void:
	{property} = value
""".format(data)
	
	data["code"] = code
	
	return data


##  返回 Get 方法
## @property  属性名
## @type  类型
## @return 返回生成的 Get 方法
func generate_get_method(
	property: String, 
	type: String = ""
) -> Dictionary:
	# 清除两边其他字符
	var method_property = ""
	if trim_both_sides_underline.pressed:
		method_property = property.strip_edges().trim_prefix("_").trim_suffix("_")
	else:
		method_property = property.strip_edges()
	
	# 生成方法与类型数据
	var method = "get_" + method_property
	type = type.strip_edges()
	if type != "":
		type = " -> %s" % type
	
	var data = {
		property = property,
		method = method,
		type = type,
	}
	var code = """func {method}(){type}:
	return {property}
""".format(data)
	
	data["code"] = code
	return data


##  返回函数行的代码 
## @return 返回类型为 RegExMatch 类型数组
func get_editor_method_list() -> Array:
	var code = edit_util.get_current_code_textedit().text
	var regex = RegEx.new()
	var pattern = (
		"\n"
		+ "(static\\s+)?"		# static
		+ "func\\s+?"			# func
		+ "(?<name>[^\\(]+)"	# 方法名
		+ "\\("
		+ "(?<arg>[^)]+)?"		# 参数
		+ "\\)"
		+ "(\\s*->\\s*(?<type>\\w+))?"	# 返回类型
		+ "\\s*:\\s*"			# 末尾冒号
	)
	regex.compile(pattern)
	return regex.search_all(code)



#==================================================
#   自定义方法
#==================================================
##  更新按钮列表
func update_list() -> void:
	# 清除列表子节点
	for child in node_checkboxlist.get_children():
		child.queue_free()
	
	# 解析代码内容，赋值到 property_list_data
	property_list_data = get_editor_property_data_list()
	
	# 添加 CheckBox 按钮并设置 data
	var regex = RegEx.new()
	regex.compile(SetGetPattern)
	var property := ""
	var setget_checkbox
	for data in property_list_data:
		property = data['name']
		setget_checkbox = SetgetCheckBox.instance()
		setget_checkbox.set_property(property)
		setget_checkbox.set_meta("data", data)
		
		# 如果有 setget 方法，则设置对应 checkbox 的 disabled 为 true
		var result = regex.search(data["setget"])
		if result:
			if result.get_string("set").strip_edges() != "":
				setget_checkbox.set_setter_disabled(true)
			if result.get_string("get").strip_edges() != "":
				setget_checkbox.set_getter_disabled(true)
		
		# 节点添加到 node_checkboxlist 节点中
		node_checkboxlist.call_deferred("add_child", setget_checkbox)


## 添加 setget 方法代码
func add_setget_method_code() -> void:
	# 属性列表
	var property = ""
	var type = ""
	var code = ""
	
	var add_count = 0
	
	# 生成 SetGet 方法
	var data 
	# 核对勾选的属性
	for child in node_checkboxlist.get_children():
		# 获取节点的属性数据
		data = child.get_meta("data")
		
		# 设置属性
		property = data["name"]
		type = data["type"]
		
		if child.is_selected_setter() || child.is_selected_getter():
			# 添加 Set 方法
			if child.is_selected_setter():
				var set_data = generate_set_method(property, type)
				code += set_data['code'] + "\n"
			
			# 添加 Get 方法
			if child.is_selected_getter():
				var get_data = generate_get_method(property, type)
				code += get_data['code'] + "\n"
			
			# 计算添加个数
			add_count += 1
	
	# 要添加的数量超过 0
	if add_count > 0:
		
		# 判断当前行代码
		var current_line_code = textedit.get_line(textedit.cursor_get_line())
		if current_line_code.strip_edges() != "":
			textedit.insert_text_at_cursor("\n\n")
		
		# 插入到代码编辑文本框中
		textedit.insert_text_at_cursor(code)
		
		# 记录光标所在位置
		var current_line = textedit.cursor_get_line()
		var current_column = textedit.cursor_get_column()
		
		## 在变量后追加 setget
		if append.pressed:
			for child in node_checkboxlist.get_children():
				# 获取节点的属性数据
				data = child.get_meta("data")
				_insert_code(data, child.is_selected_setter(), child.is_selected_getter())
		
		textedit.cursor_set_line(current_line)
		textedit.cursor_set_column(current_column)


##  插入 Setget 代码
## @data  代码数据
func _insert_code(data: Dictionary, set: bool, get: bool) -> void:
	if set == false && get == false:
		return
	
	# 代码所在行
	var line : int = data["line"]
	# 本行代码
	var line_code : String = data["code"].strip_edges()
	# 代码长度
	var line_code_count : int = line_code.length()
	
	# 设置光标位置
	textedit.cursor_set_line(line)
	
	# 存在 setget  关键字
	if data["setget"] != "":
		var add_code = ""
		
		# 添加 setget 方法
		if set:
			var set_method : String = generate_set_method(data["name"], data["type"])['method']
			var f_text := "setget "
			var after_setget : int = line_code.find(f_text) + f_text.length()
			textedit.cursor_set_column(after_setget)
			textedit.insert_text_at_cursor(set_method)
			
		if get:
			var get_method = ", " + generate_get_method(data["name"], data["type"])['method']
			
			var regex = RegEx.new()
			regex.compile(SetGetPattern)
			var result = regex.search(line_code) as RegExMatch
			
			var setget_key = "setget "
			var after_setget : int = result.get_end(1)
			var after_set_method : int =  result.get_end("set")
			textedit.cursor_set_column(after_set_method)
			textedit.insert_text_at_cursor(get_method)
			
			print("[SetGetDialog] 添加的代码： \n", get_method)
	
	# 不存在 setget 关键字
	else:
		var add_code = ""
		
		# setget 位置
		var setget_column : int = data['setget'].length()
		var has_setget_key : bool = (setget_column != -1)
		if has_setget_key:
			line_code_count = setget_column
		
		# 设置光标到代码末尾行
		textedit.cursor_set_column(line_code.length())
		
		# 添加 setget 关键字
		# 如果代码末尾没有空白字符，则插入一个
		if not line_code.right(line_code.length() - 1) in [" ", "\t"]:
			add_code += " "
		add_code += "setget "
	
		# 添加 setget 方法
		if set:
			add_code += generate_set_method(data["name"], data["type"])['method']
		if get:
			add_code += ", " + generate_get_method(data["name"], data["type"])['method']
		
		# 插入代码
		textedit.insert_text_at_cursor(add_code)



#==================================================
#   连接信号
#==================================================
# 点击 OK
func _on_OK_pressed() -> void:
	add_setget_method_code()
	self.hide()

# 点击取消
func _on_Cancel_pressed() -> void:
	self.hide()

## 点击全选
func _on_SelectAll_pressed() -> void:
	for child in node_checkboxlist.get_children():
		child.set_select_all(true)

## 点击取消全选
func _on_CancelSelectAll_pressed() -> void:
	for child in node_checkboxlist.get_children():
		child.set_select_all(false)


func _on_Setter_pressed() -> void:
	for child in node_checkboxlist.get_children():
		child.set_setter_pressed(setter.pressed)


func _on_Getter_pressed() -> void:
	for child in node_checkboxlist.get_children():
		child.set_getter_pressed(getter.pressed)


func _on_SetgetDialog_visibility_changed() -> void:
	if self.visible:
		setter.pressed = false
		getter.pressed = false
